﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
using System.Windows.Forms;
using Microsoft.Data.Caching.AdminApi;
using Microsoft.Data.Caching.Commands;
using VelocityAdminShell.CacheOperations;

namespace VelocityAdministrationShell
{
    public partial class Shell : Form
    {
        #region Consts

        private const string HostInfo = "HostInfo";
        private const string HostConfig = "HostConfig";
        private const string HostCacheStats = "HostCacheStats";
        private const string NamedCacheStats = "NamedCacheStats";
        private const string CacheInfo = "CacheInfo";

        #endregion

        #region Properties

        /// <summary>
        /// The current runspace for the Velocity cmdlet
        /// </summary>
        Runspace CurrentRunSpace { get; set; }

        /// <summary>
        /// The oprations that the console support
        /// </summary>
        List<CacheOperation> EnabledCacheOperations { get; set; }

        #endregion

        #region Ctor

        public Shell()
        {
            InitializeComponent();
            CreateCacheOperations();
        }

        #endregion

        #region Events

        private void buttonRunScript_Click(object sender, EventArgs e)
        {
            try
            {
                txtOutput.Clear();
                txtOutput.Text = RunChosenScript();
            }
            catch (Exception error)
            {
                txtOutput.Text += String.Format("\r\nError in script : {0}\r\n", error.Message);
            }
        }

        private void cbxCacheOperations_SelectedIndexChanged(object sender, EventArgs e)
        {
            var item = (CacheOperation)cbxCacheOperations.SelectedItem;
            if (item != null)
            {
                pnlOperationParameters.Visible = lblOperationParameters.Visible = item.EnabledParameters.Count != 0;
                CreateOperationParameterPanel(item);
            }
        }

        #endregion

        #region Methods

        private void CreateOperationParameterPanel(CacheOperation operation)
        {
            pnlOperationParameters.Controls.Clear();
            foreach (var parameter in operation.EnabledParameters)
            {
                CreateParameterLabel(parameter);
                CreateParameterTextbox(parameter);
            }
        }

        private void CreateParameterTextbox(CacheOperationParameter parameter)
        {
            var txt = new TextBox();
            txt.Name = string.Format("txt{0}", parameter.ParameterName);
            pnlOperationParameters.Controls.Add(txt);
        }

        private void CreateParameterLabel(CacheOperationParameter parameter)
        {
            var lbl = new Label();
            lbl.Text = string.Format("{0}:", parameter.ParameterName);
            lbl.Name = string.Format("lbl{0}", parameter.ParameterName);
            pnlOperationParameters.Controls.Add(lbl);
        }

        private void CreateCacheOperations()
        {
            EnabledCacheOperations = CacheOperationsFactory.Current.CreateAllCacheOperations();
        }

        private string RunChosenScript()
        {
            Pipeline pipeLine = CurrentRunSpace.CreatePipeline();
            RunUseClusterOperation(pipeLine);

            var item = (CacheOperation)cbxCacheOperations.SelectedItem;

            Command cmd = new Command(item.Name);
            foreach (var parameter in item.EnabledParameters)
            {
                var controls = pnlOperationParameters.Controls.Find(string.Format("txt{0}", parameter.ParameterName), false);
                if (controls.Length > 0 && !string.IsNullOrEmpty(controls[0].Text))
                {
                    cmd.Parameters.Add(new CommandParameter(parameter.ParameterName, controls[0].Text));
                }
            }
            pipeLine.Commands.Add(cmd);
            Collection<PSObject> commandResults = pipeLine.Invoke();

            return ParseScriptResults(commandResults);
        }

        private string ParseScriptResults(Collection<PSObject> commandResults)
        {
            var sb = new StringBuilder();
            foreach (var commandResult in commandResults)
            {
                var type = commandResult.BaseObject.GetType();
                switch (type.Name)
                {
                    case (HostInfo):
                        {
                            BuildHostInfoResponseText(sb, commandResult);
                            break;
                        }
                    case (HostConfig):
                        {
                            BuildHostConfigresponseText(sb, commandResult);
                            break;
                        }
                    case (HostCacheStats):
                    case (NamedCacheStats):
                        {
                            BuildCacheStatsReponseText(sb, commandResult, type);
                            break;
                        }
                    case (CacheInfo):
                        {
                            BuildCacheInfoResponseText(sb, commandResult);
                            break;
                        }
                    default:
                        {
                            sb.AppendLine(commandResult.ToString());
                            break;
                        }
                }
                sb.AppendLine();
            }
            return sb.ToString();
        }

        private static void BuildHostInfoResponseText(StringBuilder sb, PSObject commandResult)
        {
            var host = ((HostInfo)commandResult.BaseObject);
            sb.AppendFormat("Host name :{0}, Host port:{1}, Host status:{2}",
                host.HostName, host.PortNo, host.Status);
        }

        private static void BuildHostConfigresponseText(StringBuilder sb, PSObject commandResult)
        {
            var config = ((HostConfig)commandResult.BaseObject);
            sb.AppendFormat("Host name :{0}, Cache port:{1}, Cluster port:{2}, High watermark:{3}, Is quorum host:{4}, Low watermark:{5}, Size:{6}",
                config.HostName, config.CachePort, config.ClusterPort,
                config.HighWatermark, config.IsQuorumHost, config.LowWatermark,
                config.Size);      
        }

        private static void BuildCacheStatsReponseText(StringBuilder sb, PSObject commandResult, Type type)
        {
            var itemCount = (long)type.InvokeMember("ItemCount", System.Reflection.BindingFlags.GetProperty, null, commandResult.BaseObject, null);
            var missCount = (long)type.InvokeMember("MissCount", System.Reflection.BindingFlags.GetProperty, null, commandResult.BaseObject, null);
            var regionCount = (long)type.InvokeMember("RegionCount", System.Reflection.BindingFlags.GetProperty, null, commandResult.BaseObject, null);
            var requestCount = (long)type.InvokeMember("RequestCount", System.Reflection.BindingFlags.GetProperty, null, commandResult.BaseObject, null);
            var size = (long)type.InvokeMember("Size", System.Reflection.BindingFlags.GetProperty, null, commandResult.BaseObject, null);
            if (type.Name.Equals(HostCacheStats))
            {
                var namedCacheCount = (long)type.InvokeMember("NamedCacheCount", System.Reflection.BindingFlags.GetProperty, null, commandResult.BaseObject, null);
                sb.AppendFormat("Item count:{0}, Miss count:{1}, Named cache count:{2}, Region count:{3}, Request count:{4}, Size:{5}", itemCount, missCount, namedCacheCount, regionCount, requestCount, size);
            }
            else
            {
                sb.AppendFormat("Item count:{0}, Miss count:{1}, Region count:{2}, Request count:{3}, Size:{4}", itemCount, missCount, regionCount, requestCount, size);
            }
        }

        private static void BuildCacheInfoResponseText(StringBuilder sb, PSObject commandResult)
        {
            var cacheInfo = ((CacheInfo)commandResult.BaseObject);
            sb.AppendFormat("Cache name:{0}", cacheInfo.CacheName);
            foreach (var hostRegion in cacheInfo.HostRegionMap)
            {
                sb.AppendFormat("Key: {0}, Value: {1}\n", hostRegion.Key, hostRegion.Value);
            }
        }

        private void CreatePowerShellRunspace()
        {
            RunspaceConfiguration rsConfig = RunspaceConfiguration.Create();
            PSSnapInException snapInException = null;
            PSSnapInInfo info = rsConfig.AddPSSnapIn(ConfigurationManager.AppSettings["PowerShellSnapIn"], out snapInException);
            CurrentRunSpace = RunspaceFactory.CreateRunspace(rsConfig);

            CurrentRunSpace.Open();
        }

        private static void RunUseClusterOperation(Pipeline pipeLine)
        {
            Command cmd = new Command("Use-CacheCluster");
            string connString = GetConnectionString();
            cmd.Parameters.Add(new CommandParameter("ConnectionString", ConfigurationManager.ConnectionStrings["VelocitySQLConnectionString"]));
            cmd.Parameters.Add(new CommandParameter("Name", ConfigurationManager.AppSettings["ClusterName"]));
            cmd.Parameters.Add(new CommandParameter("Provider", ConfigurationManager.AppSettings["Provider"]));
            pipeLine.Commands.Add(cmd);
        }

        private static string GetConnectionString()
        {
            string sqlConnString = ConfigurationManager.ConnectionStrings["VelocitySQLConnectionString"].ConnectionString;
            string fileSystemConnString = ConfigurationManager.ConnectionStrings["VelocityFileSystemConnectionString"].ConnectionString;

            if (!string.IsNullOrEmpty(sqlConnString) || !string.IsNullOrEmpty(fileSystemConnString))
            {
                if (!string.IsNullOrEmpty(sqlConnString))
                {
                    return sqlConnString;
                }
                else
                {
                    return fileSystemConnString;
                }
            }
            else
            {
                throw new ConfigurationException("No connection string provided!");
            }
        }

        #endregion

        #region Form Events

        private void Shell_Load(object sender, EventArgs e)
        {
            CreatePowerShellRunspace();
            cacheOperationBindingSource.DataSource = EnabledCacheOperations;
            CreateOperationParameterPanel((CacheOperation)cbxCacheOperations.SelectedItem);
        }

        private void Shell_FormClosing(object sender, FormClosingEventArgs e)
        {
            CurrentRunSpace.Dispose();
        }

        #endregion
    }
}
